#!/usr/bin/env bash

# Copyright 2009 Red Hat Inc., Durham, North Carolina.
# All Rights Reserved.
#
# OpenScap Testing Helpers.
#
# Authors:
#      Ondrej Moris <omoris@redhat.com>

# Normalized path.
PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin

PREFERRED_PYTHON=@PREFERRED_PYTHON_PATH@

# Some of the tests rely on the "C" locale and would fail with some locales.
LC_ALL=C
export LC_ALL

OSCAP_FULL_VALIDATION=1
export OSCAP_FULL_VALIDATION

if [ -z ${CUSTOM_OSCAP+x} ] ; then
    enable_valgrind="@ENABLE_VALGRIND@"
    if [ $enable_valgrind != "OFF" ] ; then
        actualdir=@CMAKE_BINARY_DIR@
        export actualdir
        [ -z "@CMAKE_BINARY_DIR@" ] || export OSCAP="@CMAKE_SOURCE_DIR@/tests/valgrind_test.sh"
    else
        [ -z "@CMAKE_BINARY_DIR@" ] || export OSCAP="bash @CMAKE_BINARY_DIR@/run @CMAKE_BINARY_DIR@/utils/oscap"
    fi
    [ -z "@CMAKE_BINARY_DIR@" ] || export OSCAP_CHROOTABLE_EXEC="@CMAKE_BINARY_DIR@/utils/oscap-chrootable"
    [ -z "@CMAKE_BINARY_DIR@" ] || export OSCAP_CHROOTABLE="bash @CMAKE_BINARY_DIR@/run $OSCAP_CHROOTABLE_EXEC"
else
    export OSCAP=${CUSTOM_OSCAP}
fi

export XMLDIFF="@CMAKE_SOURCE_DIR@/tests/xmldiff.pl"

if ! XPATH_ORIG=`command -v xpath 2>&1`; then
  echo "I require xpath tool but it's not installed. Aborting." >&2
  exit 1
fi

xpath_variant=$(perl -MXML::XPath -e 'print $XML::XPath::VERSION >= 1.34 ? "need_wrapper" : "standard"')

if [ "$xpath_variant" == "need_wrapper" ];
then
	export XPATH_ORIG
	xpath_wrapper() {
		if [ "$#" == "1" ]; then
			# read file from stdin
			xpath_expr="$1"
			"$XPATH_ORIG" -e "$xpath_expr"
		elif [ "$#" == "2" ]; then
			file="$1"
			xpath_expr="$2"
			"$XPATH_ORIG" -e "$xpath_expr" "$file"
		else
			echo "Parameters are not supported by xpath wrapper" >&2
			exit 1
		fi
	}
	export -f xpath_wrapper
	export XPATH=xpath_wrapper
else
	export XPATH="$XPATH_ORIG"
fi

# Overall test result.
result=0

# Set-up testing environment.
function test_init {
    :
}

# Execute test and report its results.
function test_run {
    printf "+ %-60s\n" "$1";
    echo -e "TEST: $1" >&2;
    shift
    ( exec 1>&2 ; eval "$@" )
    ret_val=$?
    if [ $ret_val -eq 0 ]; then
	echo -e "RESULT: PASSED\n" >&2
	return 0;
    elif [ $ret_val -eq 1 ]; then
	result=$(($result + $ret_val))
	echo -e "RESULT: FAILED\n" >&2
	return 1;
    elif [ $ret_val -eq 255 ]; then
	echo -e "RESULT: SKIPPED\n" >&2
	return 0;
    else
	result=$(($result + $ret_val))
	echo -e "RESULT: WARNING (unknown exist status $ret_val)\n" >&2
	return 1;
    fi
}

# Clean-up testing environment.
function test_exit {
    if [ $# -eq 1 ]
    then
        ( exec 1>&2 ; eval "$@" )
    fi

    [ $result -eq 0 ] && exit 0
    exit 1
}

# Check if requirements are in a path, use it as follows:
# require 'program' || return 255
function require {
    eval "which $1 > /dev/null 2>&1"    
    if [ ! $? -eq 0 ]; then	
        echo -e "No '$1' found in $PATH!\n" 
	return 1; # Test is not applicable.
    fi
    return 0
}

# Check if probe exists, use it as follows:
# probecheck 'probe' || return 255
function probecheck {
	if ! $OSCAP --version | grep "\<"$1"\>" >/dev/null ; then
		echo -e "Probe $1 does not exist!\n"
		return 255 # Test is not applicable.
	fi
    return 0
}

# Check for package names and return a version number
function package_version {
    # loop through multiple potential package names
    # return first version number found
    for package in $@; do
        ver=""

        # check rpm for package version first
        if [ -f "/usr/bin/rpm" ]; then
            ver=$(rpm -q $package --qf="%{version}" 2> /dev/null)

            # rpm returns error messages on stdout, check return code
            if [ ! "$?" -eq "0" ]; then
                ver=""
            fi
        fi

        # fall back to dpkg for debian systems
        if [ "${ver}" == "" ] && [ -f "/usr/bin/dpkg-query" ]; then
            # for Debian-based systems, return the upstream version
            ver="$(dpkg-query -f '${source:Upstream-Version}' -W $package 2> /dev/null)"
        fi

        # return the first match found
        if [ "${ver}" != "" ]; then
            echo "${ver}"
            return 0
        fi
    done

    # package not found
    if [ "${ver}" == "" ]; then
        return 255
    fi
}

function verify_results {

    require "grep" || return 255

    local ret_val=0;
    local TYPE="$1"
    local CONTENT="$2"
    local RESULTS="$3"
    local COUNT="$4" 
    local FULLTYPE="definition"
    
    [ $TYPE == "tst" ] && FULLTYPE="test"

    ID=1
    while [ $ID -le $COUNT ]; do
	
	CON_ITEM=`grep "id=\"oval:[[:digit:]]\+:${TYPE}:${ID}\"" $CONTENT`
	RES_ITEM=`grep "${FULLTYPE}_id=\"oval:[[:digit:]]\+:${TYPE}:${ID}\"" $RESULTS`
	OVAL_ID=`echo ${CON_ITEM} | grep -o "oval:[[:digit:]]\+:${TYPE}:${ID}"`
	if (echo $RES_ITEM | grep "result=\"true\"") >/dev/null; then
	    RES="TRUE"
	elif (echo $RES_ITEM | grep "result=\"false\"" >/dev/null); then
	    RES="FALSE"
	else
	    RES="ERROR"
	fi
	
	if (echo $CON_ITEM | grep "comment=\"true\"" >/dev/null); then
	    CMT="TRUE"
	elif (echo $CON_ITEM | grep "comment=\"false\"" >/dev/null); then
	    CMT="FALSE"
	else
	    CMT="ERROR"
	fi
	
	if [ ! $RES = $CMT ]; then
	    echo "Result of ${OVAL_ID} should be ${CMT} and is ${RES}"
	    ret_val=$(($ret_val + 1))
	fi

	ID=$(($ID+1))
    done

    return $([ $ret_val -eq 0 ])
}

assert_exists() {
        real_cnt="$($XPATH $result 'count('"$2"')' 2>/dev/null)"
        if [ "$real_cnt" != "$1" ]; then
                echo "Failed: expected count: $1, real count: $real_cnt, xpath: '$2'"
                return 1
        fi
}

# $1: The chroot directory
set_chroot_offline_test_mode() {
	if test -n "$_OSCAP_BEFORE"; then
		echo "Already in offline test mode!" >&2
		return
	fi
	if test -x "$OSCAP_CHROOTABLE_EXEC"; then
		if ! getcap "$OSCAP_CHROOTABLE_EXEC" | grep -q 'cap_sys_chroot+ep'; then
			echo "Skipping test '${FUNCNAME[1]}' as '$OSCAP_CHROOTABLE_EXEC' doesn't have the chroot capability." >&2
			return 255
		fi
		_OSCAP_BEFORE="$OSCAP"
		OSCAP="$OSCAP_CHROOTABLE"
	elif test $(id -u) -eq 0; then
		: # Running offline tests as root is acceptable too
	else
		echo "Skipping test '${FUNCNAME[1]}' as '$OSCAP_CHROOTABLE_EXEC' oscap which is supposed to have chroot capability doesn't exist." >&2
		return 255
	fi
	set_offline_chroot_dir "$1"
	return 0
}

# $1: The chroot directory. If empty, unset the OSCAP_PROBE_ROOT variable
set_offline_chroot_dir() {
	if test -n "$1"; then
		export OSCAP_PROBE_ROOT="$1"
	else
		unset OSCAP_PROBE_ROOT
	fi
}

unset_chroot_offline_test_mode() {
	if test -n "$_OSCAP_BEFORE"; then
		OSCAP="$_OSCAP_BEFORE"
		_OSCAP_BEFORE=
	fi
	set_offline_chroot_dir ""
}

export -f assert_exists
